/*******************************************************************************
* Copyright 2013-2015 alladin-IT GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package at.alladin.rmbt.android.test;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.TrafficStats;
import android.os.Handler;
import android.os.Process;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import at.alladin.rmbt.android.util.AsyncHtmlStatusCodeRetriever;
import at.alladin.rmbt.android.util.AsyncHtmlStatusCodeRetriever.ContentRetrieverListener;
import at.alladin.rmbt.client.v2.task.service.WebsiteTestService;
/**
*
* @author lb
*
*/
public class WebsiteTestServiceImpl implements WebsiteTestService {
/**
* <p>
* if set to true the traffic will be recorded using
* {@link TrafficStats#getUidRxBytes(int)} and {@link TrafficStats#getUidTxPackets(int)}
* </p>
* <p>
* otherwise this service will call:
* {@link TrafficStats#getTotalRxBytes()} and {@link TrafficStats#getTotalTxPackets()}
* </p>
*/
private final static boolean USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT = true;
private WebView webView;
private final Context context;
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final AtomicBoolean hasFinished = new AtomicBoolean(false);
private final AtomicBoolean hasError = new AtomicBoolean(false);
private final AtomicInteger resourceCount = new AtomicInteger(0);
private int statusCode = -1;
private long duration = -1;
private RenderingListener listener;
private long trafficRxStart;
private long trafficTxStart;
private long trafficRxEnd;
private long trafficTxEnd;
private final Handler handler;
private int processUid;
public WebsiteTestServiceImpl(final Context context) {
this.context = context;
this.handler = new Handler(context.getMainLooper());
}
/**
*
* @return
*/
public WebsiteTestServiceImpl getInstance() {
return new WebsiteTestServiceImpl(context);
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#getHash()
*/
@Override
public String getHash() {
// TODO Auto-generated method stub
return null;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#getDownloadDuration()
*/
@Override
public long getDownloadDuration() {
return duration;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#run(java.lang.String, long)
*/
@Override
public void run(final String targetUrl, final long timeOut) {
handler.post(new Runnable() {
@Override
public void run() {
WebsiteTestServiceImpl.this.processUid = Process.myUid();
//webView.removeAllViews();
if (webView == null)
webView = new WebView(context);
webView.clearCache(true);
final long start = System.nanoTime();
System.out.println("Running WEBSITETASK " + targetUrl);
boolean isTrafficServiceSupported = USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT ?
TrafficStats.getUidRxBytes(processUid) != TrafficStats.UNSUPPORTED :
TrafficStats.getTotalRxBytes() != TrafficStats.UNSUPPORTED;
if (!isTrafficServiceSupported) {
trafficRxStart = -1;
trafficTxStart = -1;
trafficRxEnd = -1;
trafficTxEnd = -1;
}
else {
if (USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT) {
trafficTxStart = TrafficStats.getUidTxBytes(processUid);
trafficRxStart = TrafficStats.getUidRxBytes(processUid);
}
else {
trafficTxStart = TrafficStats.getTotalTxBytes();
trafficRxStart = TrafficStats.getTotalRxBytes();
}
}
Thread timeoutThread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("WEBSITETASK STARTING TIMEOUT THREAD: " + timeOut + " ms");
Thread.sleep(timeOut);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt(); // restore interrupt state
return;
}
if (!WebsiteTestServiceImpl.this.hasFinished() && listener != null) {
setEndTrafficCounter();
if (listener.onTimeoutReached(WebsiteTestServiceImpl.this)) {
System.out.println("WEBSITETESTTASK TIMEOUT");
WebsiteTestServiceImpl.this.handler.post(new Runnable() {
@Override
public void run() {
WebsiteTestServiceImpl.this.webView.stopLoading();
}
});
}
}
}
});
timeoutThread.start();
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
/*
@Override
public boolean shouldOverrideUrlLoading(WebView view,
String url) {
view.loadUrl(url);
return true;
}
*/
@Override
public void onLoadResource(WebView view, String url) {
System.out.println("getting resource: " + url + " progress: " + view.getProgress());
resourceCount.incrementAndGet();
super.onLoadResource(view, url);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
WebsiteTestServiceImpl.this.isRunning.set(false);
WebsiteTestServiceImpl.this.hasFinished.set(true);
WebsiteTestServiceImpl.this.hasError.set(false);
WebsiteTestServiceImpl.this.duration = System.nanoTime() - start;
if (WebsiteTestServiceImpl.this.trafficRxStart != -1) {
setEndTrafficCounter();
}
System.out.println("PAGE FINISHED " + targetUrl + " progress: " + view.getProgress() + "%, resources counter: " + resourceCount.get());
if (listener != null) {
listener.onRenderFinished(WebsiteTestServiceImpl.this);
}
}
@Override
public void onPageStarted(final WebView view, String url, Bitmap favicon) {
WebsiteTestServiceImpl.this.isRunning.set(true);
WebsiteTestServiceImpl.this.hasFinished.set(false);
WebsiteTestServiceImpl.this.hasError.set(false);
if (listener != null) {
listener.onDownloadStarted(WebsiteTestServiceImpl.this);
}
System.out.println("PAGE STARTED " + targetUrl);
super.onPageStarted(view, url, favicon);
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
WebsiteTestServiceImpl.this.isRunning.set(false);
WebsiteTestServiceImpl.this.hasFinished.set(true);
WebsiteTestServiceImpl.this.hasError.set(true);
WebsiteTestServiceImpl.this.duration = System.nanoTime() - start;
if (WebsiteTestServiceImpl.this.trafficRxStart != -1) {
setEndTrafficCounter();
}
if (listener != null) {
listener.onError(WebsiteTestServiceImpl.this);
}
}
});
AsyncHtmlStatusCodeRetriever task = new AsyncHtmlStatusCodeRetriever();
task.setContentRetrieverListener(new ContentRetrieverListener() {
@Override
public void onContentFinished(Integer statusCode) {
if (statusCode == null)
statusCode = -1;
WebsiteTestServiceImpl.this.statusCode = statusCode;
if (statusCode >= 0) {
//webView.loadDataWithBaseURL(targetUrl, htmlContent, "text/html", "utf-8", null);
webView.loadUrl(targetUrl);
//webView.loadData(htmlContent, "text/html", "utf-8");
}
else {
WebsiteTestServiceImpl.this.isRunning.set(false);
WebsiteTestServiceImpl.this.hasFinished.set(true);
WebsiteTestServiceImpl.this.hasError.set(true);
WebsiteTestServiceImpl.this.duration = System.nanoTime() - start;
if (WebsiteTestServiceImpl.this.trafficRxStart != -1) {
setEndTrafficCounter();
}
if (listener != null) {
listener.onError(WebsiteTestServiceImpl.this);
}
}
}
});
task.execute(targetUrl);
//webView.loadUrl(targetUrl);
}
});
}
/**
*
*/
private void setEndTrafficCounter() {
if (USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT) {
this.trafficRxEnd = TrafficStats.getUidRxBytes(processUid);
this.trafficTxEnd = TrafficStats.getUidTxBytes(processUid);
}
else {
this.trafficRxEnd = TrafficStats.getTotalRxBytes();
this.trafficTxEnd = TrafficStats.getTotalTxBytes();
}
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#isRunning()
*/
@Override
public boolean isRunning() {
final boolean isRunning = this.isRunning.get();
return isRunning;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#hasFinished()
*/
@Override
public boolean hasFinished() {
final boolean hasFinished = this.hasFinished.get();
return hasFinished;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#setOnRenderingFinishedListener(at.alladin.rmbt.client.v2.task.WebsiteTest.RenderingFinishedListener)
*/
@Override
public void setOnRenderingFinishedListener(RenderingListener listener) {
this.listener = listener;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#hasError()
*/
@Override
public boolean hasError() {
return this.hasError.get();
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#getStatusCode()
*/
@Override
public int getStatusCode() {
return statusCode;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#getTxBytes()
*/
@Override
public long getTxBytes() {
return (trafficTxStart != -1 ? trafficTxEnd - trafficTxStart : -1);
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#getRxBytes()
*/
@Override
public long getRxBytes() {
return (trafficRxStart != -1 ? trafficRxEnd - trafficRxStart : -1);
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.WebsiteTest#getTotalTrafficBytes()
*/
@Override
public long getTotalTrafficBytes() {
return (getRxBytes() != -1 ? getRxBytes() + getTxBytes() : -1);
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.client.v2.task.service.WebsiteTestService#getResourceCount()
*/
@Override
public int getResourceCount() {
return resourceCount.get();
}
}